﻿#region

using System;
using System.Collections.Generic;
using System.Reflection;
using BTL.Technology.Framework.Caching;

#endregion

namespace BTL.Technology.Framework
{
    [Serializable]
    public class ObjectMappingInfo
    {
        private const string RootCacheKey = "ObjectCache_";
        private const string ObjectMapCacheKey = "ObjectMap_";

        #region "Constructors"

        ///-----------------------------------------------------------------------------
        ///<summary>
        ///  Constructs a new ObjectMappingInfo Object
        ///</summary>
        ///<history></history>
        ///-----------------------------------------------------------------------------
        public ObjectMappingInfo()
        {
            _properties = new Dictionary<string, PropertyInfo>();
            _propertyNames = new Dictionary<string, string>();
        }

        #endregion

        #region "Public Properties"

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   CacheKey gets the root value of the key used to identify the cached collection in the ASP.NET Cache.
        /// </summary>
        /// <history>[cnurse]	12/01/2007	Created</history>
        /// -----------------------------------------------------------------------------
        public string CacheKey
        {
            get
            {
                string cacheKey = RootCacheKey + ObjectName + "_";
                if (!string.IsNullOrEmpty(CacheByProperty))
                {
                    cacheKey += CacheByProperty + "_";
                }
                return cacheKey;
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   CacheByProperty gets and sets the property that is used to cache collections of the object. For example: Modules are cached by the "TabId" proeprty. Tabs are cached by the PortalId property.
        /// </summary>
        /// <remarks>
        ///   If empty, a collection of all the instances of the object is cached.
        /// </remarks>
        /// <history></history>
        /// -----------------------------------------------------------------------------
        public string CacheByProperty
        {
            get { return _cacheByProperty; }
            set { _cacheByProperty = value; }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   CacheTimeOutMultiplier gets and sets the multiplier used to determine how long the cached collection should be cached. It is multiplied by the Performance Setting - which in turn can be modified by the Host Account.
        /// </summary>
        /// <remarks>
        ///   Defaults to 20.
        /// </remarks>
        /// <history></history>
        /// -----------------------------------------------------------------------------
        public int CacheTimeOutMultiplier
        {
            get { return _cacheTimeOutMultiplier; }
            set { _cacheTimeOutMultiplier = value; }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   ColumnNames gets a dictionary of Database Column Names for the Object
        /// </summary>
        /// <history>[cnurse]	12/02/2007	Created</history>
        /// -----------------------------------------------------------------------------
        public Dictionary<string, string> PropertyNames
        {
            get { return _propertyNames; }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   ObjectType gets and sets the type of the object
        /// </summary>
        /// <history>[cnurse]	12/01/2007	Created</history>
        /// -----------------------------------------------------------------------------
        public string ObjectType
        {
            get { return _objectType; }
            set { _objectType = value; }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   PrimaryKey gets and sets the property of the object that corresponds to the primary key in the database
        /// </summary>
        /// <history></history>
        /// -----------------------------------------------------------------------------
        public string PrimaryKey
        {
            get { return _primaryKey; }
            set { _primaryKey = value; }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   Properties gets a dictionary of Properties for the Object
        /// </summary>
        /// <history></history>
        /// -----------------------------------------------------------------------------
        public Dictionary<string, PropertyInfo> Properties
        {
            get { return _properties; }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   TableName gets and sets the name of the database table that is used to persist the object.
        /// </summary>
        /// <history></history>
        /// -----------------------------------------------------------------------------
        public string ObjectName
        {
            get { return _objectName; }
            set { _objectName = value; }
        }

        #endregion

        private readonly Dictionary<string, PropertyInfo> _properties;
        private readonly Dictionary<string, string> _propertyNames;
        private string _cacheByProperty;
        private int _cacheTimeOutMultiplier;
        private string _objectName;
        private string _objectType;

        private string _primaryKey;

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   GetObjectMapping gets an instance of the ObjectMappingInfo class for the type. This is cached using a high priority as reflection is expensive.
        /// </summary>
        /// <param name="objType"> The type of the business object </param>
        /// <returns> An ObjectMappingInfo object representing the mapping for the object </returns>
        /// <history></history>
        /// -----------------------------------------------------------------------------
        public static ObjectMappingInfo GetObjectMapping(Type objType)
        {
            //TODO: Cache the info
            var cacheKey = ObjectMapCacheKey + objType.FullName;
            var objMap = (ObjectMappingInfo) DataCache.GetCache(cacheKey);


            //ObjectMappingInfo objMap = null;

            if (objMap == null)
            {
                //Create an ObjectMappingInfo instance
                objMap = new ObjectMappingInfo();
                objMap.ObjectType = objType.FullName;

                //Reflect on class to create Object Map
                //objMap.CacheByProperty = GetCacheByProperty(objType)
                //objMap.CacheTimeOutMultiplier = GetCacheTimeOutMultiplier(objType)

                //TODO: Following
                //objMap.PrimaryKey = GetPrimaryKey(objType);
                objMap.ObjectName = GetObjectName(objType);

                //Iterate through the objects properties and add each one to the ObjectMappingInfo's Properties Dictionary 
                foreach (PropertyInfo objProperty in objType.GetProperties())
                {
                    objMap.Properties.Add(objProperty.Name.ToUpperInvariant(), objProperty);
                    objMap.PropertyNames.Add(objProperty.Name.ToUpperInvariant(), GetPropertyName(objProperty));
                }
                //TODO: Persist to Cache
                DataCache.SetCache(cacheKey, objMap);
            }

            //Return Object Map
            return objMap;
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   GetColumnName gets the name of the Database Column that maps to the property.
        /// </summary>
        /// <param name="objProperty"> The proeprty of the business object </param>
        /// <returns> The name of the Database Column </returns>
        /// -----------------------------------------------------------------------------
        private static string GetPropertyName(PropertyInfo objProperty)
        {
            var propertyName = objProperty.Name;

            //check if a ColumnName Attribute has been used
            //Dim columnNameAttributes As Object() = objProperty.GetCustomAttributes(GetType(ColumnNameAttribute), True)
            //If (columnNameAttributes.Length > 0) Then
            //    Dim columnNameAttrib As ColumnNameAttribute = CType(columnNameAttributes(0), ColumnNameAttribute)
            //    columnName = columnNameAttrib.ColumnName
            //End If

            return propertyName;
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   GetTableName gets the name of the Database Table that maps to the object.
        /// </summary>
        /// <param name="objType"> The type of the business object </param>
        /// <returns> The name of the Database Table </returns>
        /// -----------------------------------------------------------------------------
        private static string GetObjectName(Type objType)
        {
            var objectName = string.Empty;

            //TODO: First check if a TableName Attribute has been used

            // If no attrubute then use Type Name
            if (string.IsNullOrEmpty(objectName))
            {
                objectName = objType.Name;
            }

            return objectName;
        }
    }
}